// Package cn implements parsing and printing functionality
// for coordinate notation, as defined at
// http://chessprogramming.wikispaces.com/Algebraic+Chess+Notation#Pure%20coordinate%20notation
package cn

import (
	"code.google.com/p/gochess/notation/san"
	pos "code.google.com/p/gochess/position"
	"errors"
	"fmt"
)

var notation2piece = map[string]pos.PieceType{
	"q": pos.Queen,
	"r": pos.Rook,
	"b": pos.Bishop,
	"n": pos.Knight,
}

// Parse returns the move encoded in notation for that given position.
//
// This function will not validate the returned move.
func Parse(p *pos.Position, notation string) (pos.Move, error) {

	if len(notation) < 4 || len(notation) > 5 {
		return pos.NoMove,
			errors.New(fmt.Sprintf("coordinate notation must have 4 or 5 letters, found %d", len(notation)))
	}
	// parse start square
	start, err := san.ParseSquare(notation[:2])
	if err != nil {
		return pos.NoMove, err
	}
	// parse destination square
	dst, err := san.ParseSquare(notation[2:4])
	if err != nil {
		return pos.NoMove, err
	}
	if dst == p.EnPassantTarget() {
		return pos.NewEnPassant(start, dst), nil
	}
	// handle promotions
	if len(notation[4:]) == 1 {
		promotion, ok := notation2piece[notation[4:]]
		if !ok {
			return pos.NoMove,
				errors.New(fmt.Sprintf("cn: invalid promotion character '%v', expected 'q', 'r', 'b'' or 'n'", promotion))
		}
		return pos.NewPromotion(start, dst, promotion), nil
	}
	// handle castlings
	player := p.SideToMove()
	if p.Get(start).Type() == pos.King {
		if start == pos.Castlings[player][pos.CastlingShort].KingLocation &&
			dst == pos.Castlings[player][pos.CastlingShort].KingDestination {
			return pos.NewCastling(player, pos.CastlingShort), nil

		}
		if start == pos.Castlings[player][pos.CastlingLong].KingLocation &&
			dst == pos.Castlings[player][pos.CastlingLong].KingDestination {
			return pos.NewCastling(player, pos.CastlingLong), nil

		}
	}
	return pos.NewMove(start, dst), nil
}

var promotion2notation = map[pos.PieceType]string{
	pos.NoPieceType: "",
	pos.Queen:       "q",
	pos.Rook:        "r",
	pos.Bishop:      "b",
	pos.Knight:      "n",
}

func Sprint(move pos.Move, sideToMove pos.Color) string {
	start, dst := move.Start(), move.Destination()
	if move.Castling() != pos.NoCastling {
		castling := pos.Castlings[sideToMove][move.Castling()]
		start, dst = castling.KingLocation, castling.KingDestination
	}
	return san.SprintSquare(start) + san.SprintSquare(dst) + promotion2notation[move.Promotion()]
}
